iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0
Software Development

從零開始Reactive Programming- Spring系列 第 26

[Day 25] Reactive Programming - Spring WebFlux(R2DBC)

  • 分享至 

  • xImage
  •  

前言

在上一個範例中,是寫死回傳的內容,顯然在現實生活中應該是不會有公司讓你可以這樣做的,而當我們的Controller開始Reactive了,背後的data store當然也要reactive起來。

R2DBC

一開始的時候其實Spring WebFlux並不支援關聯式資料庫,只能使用NoSQL或是Redis,但還是有許多的業務場景是更適合使用RDB的,這時候Spring推出 Reactive Relational Database Connectivity 簡稱R2DBC,跟JDBC一樣是DB driver,但它並不是建立在JDBC或是其他api之上,因為JDBC是blocking的api,最後與我們熟悉的Spring Data 結合成為 Spring Data R2DBC
https://ithelp.ithome.com.tw/upload/images/20211009/20141418zFrVipEWFw.png

Support

  • H2 (io.r2dbc:r2dbc-h2)
  • MariaDB (org.mariadb:r2dbc-mariadb)
  • Microsoft SQL Server (io.r2dbc:r2dbc-mssql)
  • MySQL (dev.miku:r2dbc-mysql)
  • jasync-sql MySQL (com.github.jasync-sql:jasync-r2dbc-mysql)
  • Postgres (io.r2dbc:r2dbc-postgresql)
  • Oracle (com.oracle.database.r2dbc:oracle-r2dbc)

Example

事先準備

build.gralde 多補上兩個dependencies

implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
runtimeOnly 'dev.miku:r2dbc-mysql'

這次開始的範例需要用到mySql,這邊提供docker image(docker hub)的版本,
init.sql須放在與stack.yml同一層

CREATE DATABASE IF NOT EXISTS test; 
USE test;

stack.yml主要就是有一個可以init的sql執行以及有一個8081的ui介面。

# Use root/example as user/password credentials 
version: '3.1' 
services: 
  db: 
    image: mysql 
    command: --default-authentication-plugin=mysql_native_password --init-file /data/application/init.sql 
    restart: always 
    volumes: 
        - ./init.sql:/data/application/init.sql 
    environment: 
      MYSQL_ROOT_PASSWORD: example 
    ports: 
      - 3306:3306 
  adminer: 
    image: adminer 
    restart: always 
    ports: 
      - 8081:8080

application.properties將設定帳號密碼同上面DB設定

spring.r2dbc.url=r2dbc:mysql://localhost:3306/test
spring.r2dbc.username=root
spring.r2dbc.password=example

Class

Greeting.java Entity,跟Spring data 相同,有@Id 就會自然被Spring視作為DB物件。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Greeting {
  @Id
  private Long id;
  private String message;

  public Greeting(String message) {
    this.message = message;
  }

}

GreetingRepository Repository 一樣有很方便的CrudRepository 可以使用,用法基本上是一樣的。

public interface GreetingRepository extends ReactiveCrudRepository<Greeting, Long> {
  Mono<Greeting> findById(Long id);

  Flux<Greeting> findAll();
  Flux<Greeting> findByMessage(String message);

  Mono<Void> save(Mono<Greeting> greeting);
}

\src\main\resources\schema.sql 看文件沒有找到會自動產生table,可能還沒支援,不過有提供ini.sql的方式,先準備schema.sql在resource目錄下

CREATE TABLE IF NOT EXISTS  greeting (id bigint PRIMARY KEY AUTO_INCREMENT, message VARCHAR(255));

在任意地方放上這個initializer,建議是可以放在命名為DbConfiguration之類的地方統一管理,這邊測試就直接放在WebFluxGuideApplication底下

@Bean
ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {

  ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
  initializer.setConnectionFactory(connectionFactory);
  initializer.setDatabasePopulator(
      new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));

  return initializer;
}

最後測試一下

@Bean
public CommandLineRunner demo(GreetingRepository repository) {

  return (args) -> {
    // save a few Greeting
    repository
        .saveAll(
            Arrays.asList(
                new Greeting("Hello"),
                new Greeting("Hello"),
                new Greeting("Yo"),
                new Greeting("Nice to meet you"),
                new Greeting("Hi")))
        .blockLast(Duration.ofSeconds(10));

    // fetch all Greeting
    log.info("Greeting found with findAll():");
    log.info("-------------------------------");
    repository
        .findAll()
        .doOnNext(
            greeting -> {
              log.info(greeting.toString());
            })
        .blockLast(Duration.ofSeconds(10));

    log.info("");

    // fetch an individual Greeting by ID
    repository
        .findById(1L)
        .doOnNext(
            greeting -> {
              log.info("Greeting found with findById(1L):");
              log.info("--------------------------------");
              log.info(greeting.toString());
              log.info("");
            })
        .block(Duration.ofSeconds(10));

    // fetch Greeting by last name
    log.info("Greeting found with findByMessage('Hello'):");
    log.info("--------------------------------------------");
    repository
        .findByMessage("Hello")
        .doOnNext(
            hello -> {
              log.info(hello.toString());
            })
        .blockLast(Duration.ofSeconds(10));
    log.info("");
  };
}

https://ithelp.ithome.com.tw/upload/images/20211009/20141418RRB1BCBaBQ.png

結語

今天簡單的將Reactive Programming推進到了DB的世界,下一篇會補充說明。

資料來源

上一篇
[Day 24] Reactive Programming - Spring WebFlux(Router)
下一篇
[Day 26] Reactive Programming - Spring WebFlux(R2DBC Repositories)
系列文
從零開始Reactive Programming- Spring32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
chichi
iT邦新手 2 級 ‧ 2021-10-10 15:09:39

mybatis 不知道有沒有支援

官方沒有提到,但看起來有人在開發中
https://github.com/linux-china/mybatis-r2dbc

我要留言

立即登入留言